#include <stdint.h>
#include <cstring>
#include "texture.h"
#include "bitmap.h"
#include "error.h"
#include "file.h"

RGBcolor useRGB(unsigned char r,unsigned char g,unsigned char b){
    RGBcolor out;
    out.R = r;
    out.G = g;
    out.B = b;
    return out;
}

texture* texture::current = NULL;
unsigned int texture::getCurWidth(){
    if( !current ) return 0;
    return current->getWidth();
}
unsigned int texture::getCurHeight(){
    if( !current ) return 0;
    return current->getHeight();
}

//texture::texture(): texture_data(0), width(0), height(0){}
texture::texture(GLuint ID, unsigned int wid, unsigned int hei):
    texture_data(ID), width(wid), height(hei){}
texture::~texture(){
    if( (texture_data) ) glDeleteTextures(1,&texture_data);
}

unsigned int texture::getWidth(){ return width; }
unsigned int texture::getHeight(){ return height; }
GLuint texture::texData(){ return texture_data; }

void texture::bind(texture* tex){
    if( !tex ) return;
    glBindTexture(GL_TEXTURE_2D,tex->texData());
    current = tex;
}

texture* texture::texFromBMP(char* infile,RGBcolor* col){

    error::log(error::con_only,"Loading of bitmap file %s...",infile);

    bitmap bmp(0,0,(col)?32:24);
    if( !bmp.loadFile(infile) ) return NULL;

    if( col ){
        //error::log(error::con_only,"Applying transparency for #%0.2x%0.2x%0.2x...\n",col->R,col->G,col->B);
        unsigned int bmp_size = bmp.getHeight() * bmp.getWidth();
        for( unsigned int i = 0; i < bmp_size; i++ ){
            if( *(bmp.Data()+i*4) == col->R &&
                *(bmp.Data()+i*4+1) == col->G &&
                *(bmp.Data()+i*4+2) == col->B )
                *(bmp.Data()+i*4+3) = 0x00;
            else
                *(bmp.Data()+i*4+3) = 0xff;
        }
    }

    GLuint texid;
    glGenTextures(1,&texid);
    glBindTexture( GL_TEXTURE_2D, texid );
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
	glPixelStorei(GL_PACK_ALIGNMENT,1);
    switch( bmp.getBpp() ){
        case 24:
            //error::log(error::con_only,"24 bit BMP\n",infile);
            glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, bmp.getWidth(), bmp.getHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, bmp.Data() ); break;
        case 32:
            //error::log(error::con_only,"32 bit BMP\n",infile);
            glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, bmp.getWidth(), bmp.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, bmp.Data() ); break;
        default:
            error::log(error::log_warn,"Error loading %s\n",infile);
    }

    error::log(error::con_only,"Done\n");

    return new texture(texid,bmp.getWidth(),bmp.getHeight());

}

//animated_texture():

animated_texture::animated_texture(unsigned short fn,unsigned int wid,unsigned int hei):
frame_number(fn),width(wid),height(hei){
    texture_data = new GLuint[fn];
    delays = new unsigned short[fn];
    memset(texture_data,0,fn*sizeof(GLuint));
    memset(delays,0,fn*sizeof(unsigned short));
}
animated_texture::~animated_texture(){
    if( (texture_data) ) glDeleteTextures(frame_number,texture_data);
}

unsigned int animated_texture::getWidth(){ return width; }
unsigned int animated_texture::getHeight(){ return height; }
unsigned short animated_texture::getFrNum(){ return frame_number; }
GLuint*  animated_texture::lptexData(unsigned short i){ return texture_data + i * sizeof(GLuint); }
GLuint  animated_texture::texData(unsigned short i){ return texture_data[i]; }
unsigned short* animated_texture::lpdelay(unsigned short i){ return delays + i * sizeof(unsigned short); }
unsigned short animated_texture::delay(unsigned short i){ return delays[i]; }

void animated_texture::bind(animated_texture* tex,unsigned short i){
    if( !tex || i >= tex->getFrNum() ) return;
    glBindTexture(GL_TEXTURE_2D,tex->texData(i));
}
animated_texture* animated_texture::texFromANM(char* infile,RGBcolor* col){

    error::log(error::con_only,"Loading of animation file %s...",infile);

    file inf;
    if( !inf.open(infile) ){
        error::log(error::log_err,"Can not find %s...",infile);
        return NULL;
    }
    anm_header hdr;
    inf.read(&hdr,sizeof(anm_header),1);
    if( strncmp((char*)&hdr.anm_str,"ANM",3) ){
        error::log(error::log_err,"%s's magic number not corresponding to animation file, aborting...",infile);
        return NULL;
    }

    animated_texture* stackout = new animated_texture( hdr.anm_frame_num, hdr.anm_width, hdr.anm_height );
    inf.read(stackout->lpdelay(),sizeof(unsigned short),hdr.anm_frame_num);

    bitmap stbmp;

    glGenTextures(hdr.anm_frame_num,stackout->lptexData());

    for( unsigned short f = 0; f < hdr.anm_frame_num; f++ ){
        stbmp = bitmap(0,0,(col)?32:24);
        stbmp.loadSFile(&inf,hdr.anm_width,hdr.anm_height,hdr.anm_bpp);
        if( col ){
            //error::log(error::con_only,"Applying frame %d transparency for #%0.2x%0.2x%0.2x...\n",f,col->R,col->G,col->B);
            unsigned int bmp_size = stbmp.getHeight() * stbmp.getWidth();
            for( unsigned int i = 0; i < bmp_size; i++ ){
                if( *(stbmp.Data()+i*4) == col->R &&
                    *(stbmp.Data()+i*4+1) == col->G &&
                    *(stbmp.Data()+i*4+2) == col->B )
                    *(stbmp.Data()+i*4+3) = 0x00;
                else
                    *(stbmp.Data()+i*4+3) = 0xff;
            }
        }
        animated_texture::bind( stackout, f );
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
        glPixelStorei(GL_PACK_ALIGNMENT,1);
        switch( stbmp.getBpp() ){
        case 24:
            //error::log(error::con_only,"24 bit BMP\n",infile);
            glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, stbmp.getWidth(), stbmp.getHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, stbmp.Data() ); break;
        case 32:
            //error::log(error::con_only,"32 bit BMP\n",infile);
            glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, stbmp.getWidth(), stbmp.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, stbmp.Data() ); break;
        default:
            error::log(error::log_warn,"Error loading %s\n",infile);
            delete[] stackout;
            return NULL;
        }
    }

    error::log(error::con_only,"Done\n",infile);

    return stackout;
}

motion_controller::motion_controller(animated_texture* tex):
support( tex ), lastframe( 0 ), framestart( clock() ),
backlooping( false ), backloop( false ), loop_delay(0),speed(1.0){}

void motion_controller::bind(){
    if( !support ) return;
    clock_t now = clock();
    unsigned int delta = now - framestart;
    unsigned int delay = (unsigned int)(((float)support->delay(lastframe))/speed);
    if( !lastframe ) delay += loop_delay;
    if( delta >= delay ){
        framestart = now - delta + delay ;
        if( backloop && backlooping ) lastframe--;
        else lastframe++;
        if( lastframe >= support->getFrNum() ){
                if( backloop ){
                    backlooping = true;
                    lastframe -= 2;
                } else lastframe = 0;
        }
        if( lastframe < 0 ){
            backlooping = false;
            lastframe += 2;
        }
    }
    animated_texture::bind( support, lastframe );
}
void motion_controller::zero(){
    lastframe = 0;
    framestart = clock();
}
